﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace MVVMFramework.View
{
    [TemplatePart(Name = "PART_ContentHolder", Type = typeof(Panel))]
    public class Rating : Control
    {
        private Panel panel;
        private readonly Dictionary<int, ToggleButton> stars;

        #region Ctors

        static Rating()
        {
            Type ownerType = typeof(Rating);
            DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(ownerType));
        }

        public Rating()
        {
            stars = new Dictionary<int, ToggleButton>();
        }

        #endregion

        #region Properties

        public static readonly DependencyProperty RatingValueProperty =
            DependencyProperty.Register("RatingValue", typeof(int), typeof(Rating),
            new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(ratingValuePropertyChangedCallback)));

        public int RatingValue
        {
            get
            {
                return (int)GetValue(RatingValueProperty);
            }
            set
            {
                SetValue(RatingValueProperty, value);
            }
        }

        private static void ratingValuePropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            Rating parent = (Rating)sender;
            parent.setRating((int)e.NewValue);
        }

        public static readonly DependencyProperty MaxRatingValueProperty =
            DependencyProperty.Register("MaxRatingValue", typeof(int), typeof(Rating), new PropertyMetadata(5, maxRatingValuPropertyChangedCallback));

        private static void maxRatingValuPropertyChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            ((Rating)obj).initializePanel();
        }

        public int MaxRatingValue
        {
            get
            {
                return (int)GetValue(MaxRatingValueProperty);
            }
            set
            {
                SetValue(MaxRatingValueProperty, value);
            }
        }

        public static readonly DependencyProperty StartTemplateProperty =
            DependencyProperty.Register("StartTemplate", typeof(ControlTemplate), typeof(Rating), new PropertyMetadata(default(ControlTemplate)));

        public ControlTemplate StartTemplate
        {
            get
            {
                return (ControlTemplate)GetValue(StartTemplateProperty);
            }
            set
            {
                SetValue(StartTemplateProperty, value);
            }
        }

        #endregion

        public override void OnApplyTemplate()
        {
            panel = GetTemplateChild("PART_ContentHolder") as Panel;

            initializePanel();

            base.OnApplyTemplate();
        }

        private void initializePanel()
        {
            clearStars();

            if (panel != null)
            {
                for (int i = 0; i < MaxRatingValue; i++)
                    createToggleButton(i + 1);

                setRating(RatingValue);
            }
        }

        private void setRating(int value)
        {
            foreach (var source in stars.Where(s => s.Key <= value))
                source.Value.IsChecked = true;

            foreach (var source in stars.Where(s => s.Key > value))
                source.Value.IsChecked = false;
        }

        private void createToggleButton(int ratingValue)
        {
            ToggleButton toggleButton = new ToggleButton();
            toggleButton.Click += toggleButton_Click;
            toggleButton.Tag = ratingValue;
            toggleButton.Template = StartTemplate;

            stars[ratingValue] = toggleButton;
            panel.Children.Add(toggleButton);
        }

        private void clearStars()
        {
            foreach (var star in stars)
                star.Value.Click -= toggleButton_Click;

            stars.Clear();
        }

        private void toggleButton_Click(Object sender, RoutedEventArgs e)
        {
            ToggleButton button = sender as ToggleButton;
            if (button == null)
                return;

            var star = stars.FirstOrDefault(v => v.Value == button);

            int newRating = star.Key;
            if (button.IsChecked == true || newRating < RatingValue)
                RatingValue = newRating;
            else
                RatingValue = newRating - 1;

            e.Handled = true;
        }
    }
}
